package org.shagin.jfin.ui.date;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;

import org.jdesktop.swingx.JXDatePicker;
import org.shagin.jfin.core.Debug;
import org.shagin.jfin.date.DateUtils;
import org.shagin.jfin.date.cond.DateRange;
import org.shagin.jfin.ui.UI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The Class JDateRange.
 */
@SuppressWarnings("serial")
public class JDateRange extends JPanel {

    private static final Logger log = LoggerFactory.getLogger(JDateRange.class);

    private DateRange dateRange = new DateRange();
    private JCheckBox cbStart = new JCheckBox(getLabelFrom());
    private JCheckBox cbEnd = new JCheckBox(getLabelTo());
    private JLabel lblStart = new JLabel(getLabelFrom());
    private JLabel lblEnd = new JLabel(getLabelTo());
    private JXDatePicker dpStart = new JXDatePicker();
    private JXDatePicker dpEnd = new JXDatePicker();
    protected EventListenerList listenerList = new EventListenerList();
    private ChangeListener cbStartChangeListener;
    private ChangeListener cbEndChangeListener;
    private ActionListener dpStartActionListener;
    private ActionListener dpEndActionListener;

    /**
     * Instantiates a new j date range.
     */
    public JDateRange() {
        super();
        setLayout(new FlowLayout(FlowLayout.CENTER, UI.HGAP, UI.VGAP));
        add(cbStart);
        add(lblStart);
        add(dpStart);
        add(cbEnd);
        add(lblEnd);
        add(dpEnd);
        setOpenDatesEnabled(true);
        initEvents();
        addEvents();
    }

    // TODO Localize
    private static String getLabelFrom() {
        return "c";
    }

    private static String getLabelTo() {
        return "по";
    }

    /**
     * State changed.
     * 
     * @param dp the dp
     */
    private void stateChanged(JXDatePicker dp) {
        Debug.traceEnter(log);
        if (DateUtils.isEqual(dp.getDate(), DateUtils.CLOSE_DATE)
                || DateUtils.isEqual(dp.getDate(), DateUtils.OPEN_DATE)) {
            dp.setDate(DateUtils.now());
        }
    }

    /**
     * Inits the events.
     */
    private void initEvents() {

        cbStartChangeListener = new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                dpStart.setEnabled(cbStart.isSelected());
                JDateRange.this.stateChanged(dpStart);
                fireChangeEvent();
            }
        };

        cbEndChangeListener = new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                dpEnd.setEnabled(cbEnd.isSelected());
                JDateRange.this.stateChanged(dpEnd);
                fireChangeEvent();
            }
        };

        dpStartActionListener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (dpStart.getDate().after(dpEnd.getDate())) {
                    dpEnd.setDate(dpStart.getDate());
                }
                fireChangeEvent();
            }
        };

        dpEndActionListener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (dpEnd.getDate().before(dpStart.getDate())) {
                    dpStart.setDate(dpEnd.getDate());
                }
                fireChangeEvent();
            }
        };
    }

    /**
     * Adds the events.
     */
    private void addEvents() {
        cbStart.addChangeListener(cbStartChangeListener);
        cbEnd.addChangeListener(cbEndChangeListener);
        dpStart.addActionListener(dpStartActionListener);
        dpEnd.addActionListener(dpEndActionListener);
    }

    /**
     * Removes the events.
     */
    private void removeEvents() {
        cbStart.removeChangeListener(cbStartChangeListener);
        cbEnd.removeChangeListener(cbEndChangeListener);
        dpStart.removeActionListener(dpStartActionListener);
        dpEnd.removeActionListener(dpEndActionListener);
    }

    /**
     * Gets the date range.
     * 
     * @return the date range
     */
    public DateRange getDateRange() {
        if (!cbStart.isSelected())
            dateRange.setClosed();
        else
            dateRange.setStart(dpStart.getDate());

        if (!cbEnd.isSelected())
            dateRange.setOpened();
        else
            dateRange.setEnd(dpEnd.getDate());

        return dateRange;
    }

    /**
     * Sets the date range.
     * 
     * @param dateRange the new date range
     */
    public void setDateRange(final DateRange dateRange) {
        Debug.traceEnter(log, dateRange);
        removeEvents();
        this.dateRange = dateRange;
        dpStart.setDate(dateRange.getStart());
        dpEnd.setDate(dateRange.getEnd());
        cbStart.setSelected(!dateRange.isCloseDate());
        cbEnd.setSelected(!dateRange.isOpenDate());
        dpStart.setEnabled(cbStart.isSelected());
        dpEnd.setEnabled(cbEnd.isSelected());
        addEvents();
        fireChangeEvent();
    }

    /**
     * Adds the change listener.
     * 
     * @param listener the listener
     */
    public void addChangeListener(DateRangeListener listener) {
        if (listener == null)
            return;
        listenerList.add(DateRangeListener.class, listener);
    }

    /**
     * Removes the change listener.
     * 
     * @param listener the listener
     */
    public void removeChangeListener(DateRangeListener listener) {
        if (listener == null)
            return;
        listenerList.remove(DateRangeListener.class, listener);
    }

    /**
     * Fire change event.
     */
    public void fireChangeEvent() {
        Debug.traceEnter(log);
        DateRangeEvent event = new DateRangeEvent(this, getDateRange());
        for (DateRangeListener listener : listenerList
                .getListeners(DateRangeListener.class)) {
            listener.dateRangeChanged(event);
        }
    }

    /**
     * Sets the open dates enabled.
     * 
     * @param b the new open dates enabled
     */
    public void setOpenDatesEnabled(boolean b) {
        removeEvents();
        if (b) {
            cbStart.setSelected(!dateRange.isCloseDate());
            cbEnd.setSelected(!dateRange.isOpenDate());
        } else {
            cbStart.setSelected(true);
            cbEnd.setSelected(true);
        }
        cbStart.setVisible(b);
        cbEnd.setVisible(b);
        lblStart.setVisible(!b);
        lblEnd.setVisible(!b);
        addEvents();
    }

    /**
     * Checks if is open dates enabled.
     *
     * @return true, if is open dates enabled
     */
    public boolean isOpenDatesEnabled() {
        return cbStart.isVisible();
    }

}
